package.path=package.path..";../itemdb/?.lua"
require("curl")
require("xml")
DEFAULT_REALM = "Dalaran"

local download
do
	local temp
	local write = function (s, len)
		temp[#temp + 1] = s
		return len
	end
	function download(url)
		if VERBOSE then print("GET", url) io.flush() end
		temp = {}
		local c = curl.easy_init()
		c:setopt(curl.OPT_URL, url)
		c:setopt(curl.OPT_USERAGENT, "Firefox/2.0")
		if COOKIE then
			c:setopt(curl.OPT_HTTPHEADER, COOKIE)
		end
		c:setopt(curl.OPT_WRITEFUNCTION, write)
		local err, info = c:perform()
		if err ~= 0 then
			return
		else
			temp = table.concat(temp)
			if temp:len() > 0 then
				return temp
			end
		end
	end
end

if not NOCACHE then
	local real_download = download
	local sqlite = require("lsqlite3")
	db = sqlite.open("armory.db")
	db:exec([[
CREATE TABLE IF NOT EXISTS cache (
	url TEXT,
	content BLOB,
	time TEXT,
	PRIMARY KEY (url)
)]])
	local CACHE_TIMEOUT = CACHE_TIMEOUT or "+7 day"
	local select_stmt = db:prepare("SELECT content FROM cache WHERE url = ? AND datetime(time, '"..CACHE_TIMEOUT.."') > datetime('now')")
	local insert_stmt = db:prepare("INSERT INTO cache VALUES (?, ?, CURRENT_TIMESTAMP)")
	download = function (url)
		select_stmt:bind_values(url)
		local result = select_stmt:step()
		if result == sqlite3.ROW then
			result = select_stmt:get_value(0)
			select_stmt:reset()
			return result
		else
			select_stmt:reset()
		end
		local content = real_download(url)
		if content then
			insert_stmt:bind_values(url, content)
			insert_stmt:step()
			insert_stmt:reset()
		end
		return content
	end
end

local function getplayer(realm, name)
	local data = download(("http://eu.wowarmory.com/character-sheet.xml?r=%s&n=%s"):format(curl.escape(realm), curl.escape(name)))
	return xml.parse(data)
end

local function gettalents(realm, name)
	local data = download(("http://eu.wowarmory.com/character-talents.xml?r=%s&n=%s"):format(curl.escape(realm), curl.escape(name)))
	return xml.parse(data)
end

local function getguildinfo(realm, name)
	local data = download(("http://eu.wowarmory.com/guild-info.xml?r=%s&n=%s"):format(curl.escape(realm), curl.escape(name)))
	return xml.parse(data)
end

local function getplayerlist(realm, name, filter)
	local data = getguildinfo(realm, name)
	data = data:get_first_child("guildInfo")
	data = data:get_first_child("guild")
	data = data:get_first_child("members")
	local result = {}
	for member in data:iterate_children("character") do
		if not filter or filter(member) then
			local name = member:get_attribute("name")
			result[#result + 1] = name
		end
	end
	return { [realm] = result }
end

local base_dodge = {
	WARRIOR = 0.75,
	HUNTER = -5.45,
	PALADIN = 0.65,
	MAGE = 3.46,
	PRIEST = 3.18,
	WARLOCK = 2.03,
	SHAMAN = 1.67,
	ROGUE = -0.59,
	DRUID = 0.96,
}

local dodge_per_agi = {
	WARRIOR = 1/30,
	HUNTER = 1/25,
	PALADIN = 1/25,
	MAGE = 1/25,
	PRIEST = 1/25,
	WARLOCK= 1/25,
	SHAMAN = 1/25,
	ROGUE = 1/20,
	DRUID = 1/25,
}

local base_crit = {
	HUNTER = -1.53,
	ROGUE = -0.285,
	WARRIOR = 1.19,
	PALADIN = 0.65,
	PRIEST = 3.18,
	SHAMAN = 1.67,
	MAGE = 3.46,
	WARLOCK = 2.03,
	DRUID = 0.96,
}

local crit_from_agi = {
	HUNTER = 1/40,
	ROGUE = 1/40,
	WARRIOR = 1/33,
	PALADIN = 1/25,
	PRIEST = 1/25,
	SHAMAN = 1/25,
	MAGE = 1/25,
	WARLOCK = 1/25,
	DRUID = 1/25,
}

local base_spellcrit = {
	ROGUE = -1,
	WARRIOR = -1,
	HUNTER = 3.6,
	PALADIN = 3.34,
	PRIEST = 1.24,
	SHAMAN = 2.2,
	MAGE = 0.9,
	WARLOCK = 1.7,
	DRUID = 1.85,
}

local spellcrit_from_int = {
	ROGUE = 0,
	WARRIOR = 0,
	HUNTER = 1/80,
	PALADIN = 1/80,
	PRIEST = 1/80,
	SHAMAN = 1/80,
	MAGE = 1/80,
	WARLOCK = 1/82,
	DRUID = 1/80,
}

local combat_rating_map = {
	CR_WEAPON = 2.5,
	CR_WEAPON_DAGGER = 2.5,
	CR_WEAPON_SWORD = 2.5,
	CR_WEAPON_SWORD_2H = 2.5,
	CR_WEAPON_AXE = 2.5,
	CR_WEAPON_AXE_2H = 2.5,
	CR_WEAPON_MACE = 2.5,
	CR_WEAPON_MACE_2H = 2.5,
	CR_WEAPON_BOW = 2.5,
	CR_WEAPON_CROSSBOW = 2.5,
	CR_WEAPON_GUN = 2.5,
	CR_WEAPON_FIST = 2.5,
	CR_WEAPON_STAFF = 2.5,
	CR_WEAPON_POLEARM = 2.5,

	CR_DEFENSE = 1.5,
	CR_DODGE = 12,
	CR_PARRY = 15,
	CR_BLOCK = 5,
	CR_HIT = 10,
	CR_CRIT = 14,
	CR_RANGEDHIT = 10,
	CR_RANGEDCRIT = 14,
	CR_HASTE = 10,
	CR_RANGEDHASTE = 10,
	CR_SPELLHIT = 8,
	CR_SPELLCRIT = 14,
	CR_SPELLHASTE = 10,
	CR_RESILIENCE = 25,
	-- does this exist in game ?
	CR_HIT_TAKEN = 10,
	CR_RANGEDHIT_TAKEN = 10,
	CR_SPELLHIT_TAKEN = 8,
	CR_EXPERTISE = 2.5,
}

-- return the ratio of percent / rating point, dependant on the unit level
local function GetRatingMultiplier(level)
	if level < 9 then
		return 52
	elseif level <= 60 then
		return 52 / (level - 8)
	elseif level <= 70 then
		return 3.1951219512195124 - 0.036585365853658534 * level
	end
end

-- return the percent value of a rating, dependant on the type, rating value and unit level.
local function GetRatingBonus(type, value, level)
	local F = combat_rating_map[type]
	if not F then
		return nil
	end
	if not value then
		return 0
	end
	return value / F * GetRatingMultiplier(level)
end

local function correct_dodge_value(result)
	local value = result.dodge
	if result.class == "Rogue" then
		local index = 24 -- lightning reflexes
		local n = tonumber(result.talents:sub(index, index))
		value = value - n
	elseif result.class == "Hunter" then
		local index = 19 -- Catlike reflexes
		local n = tonumber(result.talents:sub(index, index))
		value = value - n
	elseif result.class == "Shaman" then
		local index = 29 -- Anticipation
		local n = tonumber(result.talents:sub(index, index))
		value = value - n
	end
	if result.race == "Night Elf" then
		value = value - 1
	end
	if result.buff_dodge then
		value = value - result.buff_dodge
	end
	result.dodge = value
end

local function calc_expected_values(result)
	local class = result.class:upper()
	result.expected_dodge = base_dodge[class] + result.agility * dodge_per_agi[class]
	result.expected_crit = base_crit[class] + result.agility * crit_from_agi[class]
	result.expected_spellcrit = base_spellcrit[class] + result.intellect * spellcrit_from_int[class]
end

local function get_player_info(realm, name)
	local data = getplayer(realm, name)
	
	data = data:get_first_child("characterInfo")
	local char = data:get_first_child("character")
	if not char then return end
	local tab = data:get_first_child("characterTab")
	if not tab then print("ERROR", realm, name) return end

	local result = {
		name = name,
	}

	result.level = tonumber(char:get_attribute("level"))
	result.class = char:get_attribute("class")
	result.race = char:get_attribute("race")

	local stats = tab:get_first_child("baseStats")
	local agi = stats:get_first_child("agility")

	result.agility = tonumber(agi:get_attribute("effective"))
	result.crit_from_agility = tonumber(agi:get_attribute("critHitPercent"))
	
	local int = stats:get_first_child("intellect")
	result.intellect = tonumber(int:get_attribute("effective"))
	result.spellcrit_from_int = tonumber(int:get_attribute("critHitPercent"))
	
	local defenses = tab:get_first_child("defenses")
	local dodge = defenses:get_first_child("dodge")
	local def = defenses:get_first_child("defense")
	local buffs = tab:get_first_child("buffs")
	for buff in buffs:iterate_children("spell") do
		local name = buff:get_attribute("name")
		if name == "Aspect of the Monkey" then
			result.buff_dodge = 8
		end
	end
	
	result.dodge_rating = tonumber(dodge:get_attribute("rating"))
	result.base_dodge = tonumber(dodge:get_attribute("percent"))
	result.dodge = result.base_dodge - tonumber(dodge:get_attribute("increasePercent")) - tonumber(def:get_attribute("increasePercent"))
	local talents = gettalents(realm, name)
	talents = talents:get_first_child("characterInfo")
	talents = talents:get_first_child("talentTab")
	talents = talents:get_first_child("talentTree")
	result.talents = talents:get_attribute("value")
	
	correct_dodge_value(result)
	calc_expected_values(result)
	
	
	return result
end

if arg[1] then
	local data = get_player_info("Dalaran", arg[1])
	for k, v in pairs(data) do
		print(k, v)
	end
else
	local result = {}
--[[
	local players = {
		Dalaran = {
			"Alphah", "Andarhun", "Apôphis", "Boss", "Macleod", "Radnarok", "Mignote", "Jarek",
			-- "Absalom", "Belze", "Couguar", "Couguarine", "Cybelwynd", "Darlan", "Dyandra", "Lounatrila"
			-- "Thanator", "Durin", "Thörin", "Meren",
--~ 			"Millander", "Daenerys", "Inghean", "Laerale", "Slone", "Grandspieds"
--~ 			"Shao", "Chdje", "Héra", "Priêst",
--~ 			"Flitz", "Targezed", "Littlemouche", "Mempômal",
		},
		["Kirin Tor"] = { 
--~ 			"Miniman", 
--~ 				"Ikä"
		},
		["Ner'zhul"] = { 
--~ 			"Rouaf", 
--~ 				"Zoltarus",
		},
		Archimonde = {
--~ 			"Iamtaz", "Tyidus",
--~ 			"Vixcis", "Vivic", "Ions",
		},
		["Kael'Thas"] = {
--~ 			"Lïna",
		}
	}
]]
	for realm, list in pairs(getplayerlist("Kirin Tor", "Seed", function (m)
		local l = tonumber(m:get_attribute("level"))
		local c = m:get_attribute("class")
		return true or l == 70 and c == "Warrior"
	end)) do
		for _, player in ipairs(list) do
			local data = get_player_info(realm, player)
			if data then
				result[#result + 1] = data
			end
		end
	end
	
	for _, entry in ipairs(result) do
		-- print(entry.name, entry.agility, entry.dodge, entry.crit_from_agility, entry.crit_from_agility - entry.expected_crit, entry.dodge - entry.expected_dodge)
		print(entry.name, entry.intellect, entry.spellcrit_from_int, entry.spellcrit_from_int - entry.expected_spellcrit)
	end
end
